package com.errol.generic.baiducloud.faceai;

import com.renjia.service.generic.baiducloud.basic.BaiduBasicService;
import com.renjia.service.generic.baiducloud.faceai.pojo.faceliveness.*;
import com.renjia.service.generic.baiducloud.faceai.pojo.personverify.*;
import com.renjia.service.generic.http.HttpBuilder;
import com.renjia.service.generic.http.HttpResult;
import com.renjia.service.generic.http.HttpService;
import com.renjia.service.generic.log.LogService;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.io.File;
import java.net.URLEncoder;
import java.util.Base64;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author zhang xiao bin
 * @version v1.0
 * @date 2021/03/24
 * @since v
 **/
@Service
public class BaiduFaceAiServiceImpl implements BaiduFaceAiService {

    private final BaiduFaceAiConfig config;
    private final BaiduBasicService baiduBasicService;
    private final HttpService httpService;
    private final LogService logService;

    public BaiduFaceAiServiceImpl(BaiduFaceAiConfig config,
                                  BaiduBasicService baiduBasicService,
                                  HttpService httpService,
                                  LogService logService) {
        this.config = config;
        this.baiduBasicService = baiduBasicService;
        this.httpService = httpService;
        this.logService = logService;
    }

    @Nullable
    @Override
    public BaiduFaceCodeResultDTO.Result getLivenessCode(BaiduFaceVerifyType verifyType, int codeMinLength, int codeMaxLength) {
        Assert.isTrue(codeMinLength <= codeMaxLength, "codeMinLength 必须 <= codeMaxLength");
        codeMinLength = Math.max(codeMinLength, 3);
        codeMaxLength = Math.min(codeMaxLength, 6);
        String accessToken = baiduBasicService.getAccessToken(config.getAppId(), config.getApiKey(), config.getSecretKey());
        String url = "https://aip.baidubce.com/rest/2.0/face/v1/faceliveness/sessioncode?access_token=" + accessToken;
        HttpResult<BaiduFaceCodeResultDTO> execute = httpService.execute(httpService.builder(url)
                .setConnectTimeout(10)
                .setReadTimeout(10)
                .usePost()
                .sendFormData()
                .addRequestBodyParam("type", verifyType.getCode())
                .addRequestBodyParam("min_code_length", codeMinLength)
                .addRequestBodyParam("max_code_length", codeMaxLength)
                .expectJsonOfClass(BaiduFaceCodeResultDTO.class)
                .build());
        if (execute.isSuccess() && execute.getData() != null) {
            if (Integer.valueOf(0).equals(execute.getData().getErrCode()) && execute.getData().getResult() != null) {
                // 超时时间设为 4分半
                execute.getData().getResult().setExpireTime(System.currentTimeMillis() + (6000 * 45));
                return execute.getData().getResult();
            } else {
                logService.remind("百度活体检测，获取验证码失败，errCode=" + execute.getData().getErrCode() + ",errMsg=" + execute.getData().getErrMsg());
            }
        }
        return null;
    }

    @Override
    public BaiduFaceVerifyResultDTO verifyFaceLiveness(BaiduFaceVerifyType verifyType, File videoFile, BaiduFaceCodeResultDTO.Result livenessCode,
                                                       BaiduFaceVerifyLipType verifyLipType, BaiduFaceVerifyFieldType verifyFieldType) {
        if (livenessCode == null || System.currentTimeMillis() > livenessCode.getExpireTime() || StringUtils.isEmpty(livenessCode.getSessionId())) {
            return new BaiduFaceVerifyResultDTO(BaiduFaceVerifyResultDTO.ResultStatus.ERROR_216502);
        }
        long maxSize = 20 * 1024 * 1024L;
        if (videoFile.length() > maxSize) {
            return new BaiduFaceVerifyResultDTO(BaiduFaceVerifyResultDTO.ResultStatus.ERROR_FILE_TOO_BIG);
        }
        try {
            String imageBase64 = Base64.getEncoder().encodeToString(FileUtils.readFileToByteArray(videoFile));
            if (imageBase64.length() > maxSize) {
                return new BaiduFaceVerifyResultDTO(BaiduFaceVerifyResultDTO.ResultStatus.ERROR_FILE_TOO_BIG);
            }
            String accessToken = baiduBasicService.getAccessToken(config.getAppId(), config.getApiKey(), config.getSecretKey());
            String url = "https://aip.baidubce.com/rest/2.0/face/v1/faceliveness/verify?access_token=" + accessToken;
            HttpResult<BaiduFaceVerifyResultDTO> execute = httpService.execute(httpService.builder(url)
                    .setConnectTimeout(60)
                    .setReadTimeout(60)
                    .usePost()
                    .sendFormData()
                    .addRequestBodyParam("type_identify", verifyType.name().toLowerCase())
                    .addRequestBodyParam("video_base64", imageBase64)
                    .addRequestBodyParam("session_id", livenessCode.getSessionId())
                    .addRequestBodyParam("lip_identify", verifyLipType.name())
                    .addRequestBodyParam("face_field", verifyFieldType.name().toLowerCase())
                    .expectJsonOfClass(BaiduFaceVerifyResultDTO.class)
                    .build());
            // 如果出现参数转码的异常，可以尝试 urlEncode
            logService.info("活体认证结果"+execute.toString());
            if (execute.isSuccess() && execute.getData() != null) {
                return execute.getData();
            }
        } catch (Exception e) {
            logService.error(e, "百度视频活体校验执行异常");
        }
        return new BaiduFaceVerifyResultDTO(BaiduFaceVerifyResultDTO.ResultStatus.ERROR_RUNTIME_EXCEPTION);
    }

    @Override
    public BaiduPersonVerifyV2ResultDTO verifyPersonFaceV2(String imageBase64, String idCardNumber, String personName, boolean checkPersonQuality,
                                                           boolean checkFaceLiveness, List<BaiduPersonVerifyV2FieldType> fieldTypes) {
        long maxSize = 20 * 1024 * 1024L;
        if (imageBase64.length() > maxSize) {
            return new BaiduPersonVerifyV2ResultDTO(BaiduPersonVerifyV2ResultDTO.ResultStatus.IMAGE_TOO_BIG);
        }
        try {
            String accessToken = baiduBasicService.getAccessToken(config.getAppId(), config.getApiKey(), config.getSecretKey());
            String url = "https://aip.baidubce.com/rest/2.0/face/v2/person/verify?access_token=" + accessToken;
            HttpBuilder.HttpFormBodyBuilder builder = httpService.builder(url)
                    .setConnectTimeout(30)
                    .setReadTimeout(30)
                    .usePost()
                    .sendFormData()
                    .addRequestBodyParam("image", imageBase64)
                    .addRequestBodyParam("id_card_number", idCardNumber)
                    .addRequestBodyParam("name", URLEncoder.encode(personName, "UTF-8"));
            if (checkPersonQuality) {
                builder.addRequestBodyParam("quality", "use");
            }
            if (checkFaceLiveness) {
                builder.addRequestBodyParam("faceliveness", "use");
            }
            if (fieldTypes != null && !fieldTypes.isEmpty()) {
                builder.addRequestBodyParam("ext_fields", fieldTypes.stream().map(f -> f.name().toLowerCase()).collect(Collectors.joining(",")));
            }
            HttpBuilder.HttpPlan<BaiduPersonVerifyV2ResultDTO> httpPlan = builder
                    .expectJsonOfClass(BaiduPersonVerifyV2ResultDTO.class)
                    .build();
            HttpResult<BaiduPersonVerifyV2ResultDTO> execute = httpService.execute(httpPlan);
            if (execute.isSuccess() && execute.getData() != null) {
                return execute.getData();
            }
        } catch (Exception e) {
            logService.error(e, "百度身份验证执行异常");
        }
        return new BaiduPersonVerifyV2ResultDTO(BaiduPersonVerifyV2ResultDTO.ResultStatus.EXCEPTION);
    }

    @Override
    public BaiduPersonVerifyV3ResultDTO verifyPersonFaceV3(BaiduPersonVerifyV3ImageType imageType, String image, String idCardNumber, String personName, BaiduPersonVerifyV3ControlType qualityControlType, BaiduPersonVerifyV3ControlType livenessControlType, BaiduPersonVerifyV3ControlType spoofingControlType) {
        if (BaiduPersonVerifyV3ImageType.BASE64 == imageType) {
            long maxSize = 20 * 1024 * 1024L;
            if (image.length() > maxSize) {
                return new BaiduPersonVerifyV3ResultDTO(BaiduPersonVerifyV3ResultDTO.ResultStatus.IMAGE_TOO_BIG);
            }
        }
        try {
            String accessToken = baiduBasicService.getAccessToken(config.getAppId(), config.getApiKey(), config.getSecretKey());
            String url = "https://aip.baidubce.com/rest/2.0/face/v3/person/verify?access_token=" + accessToken;
            HttpResult<BaiduPersonVerifyV3ResultDTO> execute = httpService.execute(httpService.builder(url)
                    .setConnectTimeout(30)
                    .setReadTimeout(10)
                    .usePost()
                    .sendJsonData()
                    .addRequestBodyParam("image_type", imageType.name())
                    .addRequestBodyParam("image", image)
                    .addRequestBodyParam("id_card_number", idCardNumber)
                    .addRequestBodyParam("name", personName)
                    .addRequestBodyParam("quality_control", qualityControlType.name())
                    .addRequestBodyParam("liveness_control", livenessControlType)
                    .addRequestBodyParam("spoofing_control", spoofingControlType.name())
                    .expectJsonOfClass(BaiduPersonVerifyV3ResultDTO.class)
                    .build());
            if (execute.isSuccess() && execute.getData() != null) {
                return execute.getData();
            }
        } catch (Exception e) {
            logService.error(e, "百度身份验证执行异常");
        }
        return new BaiduPersonVerifyV3ResultDTO(BaiduPersonVerifyV3ResultDTO.ResultStatus.EXCEPTION);
    }
}
